#include "user_camera.h"
#include "driver/ledc.h"
#include "esp_camera.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "esp_vfs.h"

#define TAG "user_camera"

static camera_config_t camera_config = {
    .pin_pwdn = CAM_PIN_PWDN,
    .pin_reset = CAM_PIN_RESET,
    .pin_xclk = CAM_PIN_XCLK,
    .pin_sccb_sda = CAM_PIN_SIOD,
    .pin_sccb_scl = CAM_PIN_SIOC,

    .pin_d7 = CAM_PIN_D7,
    .pin_d6 = CAM_PIN_D6,
    .pin_d5 = CAM_PIN_D5,
    .pin_d4 = CAM_PIN_D4,
    .pin_d3 = CAM_PIN_D3,
    .pin_d2 = CAM_PIN_D2,
    .pin_d1 = CAM_PIN_D1,
    .pin_d0 = CAM_PIN_D0,
    .pin_vsync = CAM_PIN_VSYNC,
    .pin_href = CAM_PIN_HREF,
    .pin_pclk = CAM_PIN_PCLK,

    // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
    .xclk_freq_hz = 10000000,
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,

    .pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
    .frame_size = FRAMESIZE_UXGA,   // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S
    // series has improved a lot, but JPEG mode always gives better frame rates.

    .jpeg_quality = 12, // 0-63, for OV series camera sensors, lower number means higher quality
    .fb_count = 1,      // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
    .fb_location = CAMERA_FB_IN_PSRAM,
    .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};

static camera_fb_t *pic;
esp_err_t init_camera(void) {
    // initialize the camera
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed");
        return err;
    }

    return ESP_OK;
}

esp_err_t get_capture(const char *filepath) {
    esp_err_t err = esp_camera_init(&camera_config);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed");
        return err;
    }

    vTaskDelay(1500 / portTICK_PERIOD_MS); // 等待摄像头稳定
    ESP_LOGI(TAG, "Refreash camera buffer...");
    pic = esp_camera_fb_get();
    esp_camera_fb_return(pic); // 释放帧缓冲区以供重用

    err = camera_capture(filepath);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Camera Init Failed");
    }

    esp_camera_deinit();

    gpio_set_level(CAM_PIN_PWDN, 1);

    return err;
}

// 捕获并保存图像
esp_err_t camera_capture(const char *filePath) {
    ESP_LOGI(TAG, "Taking picture...");

    // 获取摄像头帧缓冲区
    pic = esp_camera_fb_get();

    // 检查图像是否成功捕获
    if (!pic) {
        ESP_LOGE(TAG, "Camera capture failed!");
        return ESP_FAIL;
    }

    // 打印图像的详细参数
    ESP_LOGI(TAG, "Picture taken!");
    ESP_LOGI(TAG, "Image Buffer Address: %p", pic->buf);
    ESP_LOGI(TAG, "Image Size: %zu bytes", pic->len);
    ESP_LOGI(TAG, "Image Dimensions: %zu x %zu pixels", pic->width, pic->height);
    ESP_LOGI(TAG, "Image Format: %d", pic->format);

    // 保存图片到文件系统
    FILE *file = fopen(filePath, "wb");
    if (file == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        esp_camera_fb_return(pic);
        return ESP_FAIL;
    }

    size_t written = fwrite(pic->buf, 1, pic->len, file);
    if (written != pic->len) {
        ESP_LOGE(TAG, "Failed to write all data to file, written: %zu, expected: %zu", written, pic->len);
        fclose(file);
        esp_camera_fb_return(pic);
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Image saved to %s", filePath);
    fclose(file);

    esp_camera_fb_return(pic); // 释放帧缓冲区以供重用

    return ESP_OK;
}
